#ifndef Object_First
#define Object_First
#ifdef __GNUG__
#pragma interface
#endif

#include "Types.h"
#include "ET_varargs.h"
#include "Storage.h"
#include "Stream.h"

class Object;
class Class;
class Collection;
class AccessMembers;
class Iterator;
class _Object_dummy;

const int
	cIdNone           = -1,
	cPartAnyChange    = 27;

typedef bool (*BoolFun)(Object*, Object*, void*);
typedef Object* (*ObjPtrFun)(Object*, Object*, void*);
typedef void (Object::*ObjMemberFunc)(...);
typedef void (Object::*VoidObjMemberFunc)(...);
typedef u_char (Object::*BoolObjMemberFunc)(...);

extern int gDebug;

//---- client flags ------------------------------------------------------------

enum ObjFlags {
	eObjIsDeleted   = BIT(0),
	cObjIsShared    = BIT(1),
	eObjDefault     = 0,
	eObjLast        = 1
};

//---- private flags, clients can only test but not set them -------------------

const int
	cObjNonDeleted    =   0x01000000,
	cObjDelayChanges  =   0x02000000,
	cObjVisited       =   0x04000000,
	cObjIsProto       =   0x08000000,
	cObjIsObserved    =   0x10000000;

const int cFlagMask= 0x00ffffff;

//---- Object ------------------------------------------------------------------

class Object {
friend Class;

public:
	//---- automatically added by macro metaDef
	static Class *isa;
	friend IStream &operator>> (IStream&, Object*&);
	friend Class *_Type(Object*);
	Object(_Object_dummy*);
	virtual Class *IsA() const;
	virtual void Members(AccessMembers*);

public:
	Object(int f= eObjDefault);

	virtual ~Object();
	virtual void FreeAll();

	//---- memory allocation
	inline void *operator new(size_t sz);
	inline void operator delete(void *vp);

#   define IsKindOf(name) IsA()->isKindOf(Meta(name))

	//----- flag manipulation
	void SetFlag(int, bool);
	void SetFlag(int);
	void ResetFlag(int);
	bool TestFlag(int);
	void InvertFlag(int);

	//---- attributes
	const char *ClassName() const;
	void MarkAsDeleted();
	bool IsDeleted();
	void SetVisited();
	void ClearVisited();
	bool IsObserved();
	bool IsShared();
	void SetShared();

	//---- comparing
	virtual u_long Hash();
	virtual bool IsEqual(Object*);
	virtual int Compare(Object*);

	//---- converting, activation passivation
	virtual const char *AsString();
	virtual OStream& PrintOn(OStream&);
	virtual IStream& ReadFrom(IStream&);
	virtual Object *ReadAndMap(IStream&);

	friend OStream &operator<< (OStream&, Object&);
	friend IStream &operator>> (IStream&, Object&);
	friend IStream &LoadPtr(IStream&, Object*&, Class *staticclass);
	friend OStream& operator<< (OStream&, Object*);

	//---- cloning
	Object* New();
	virtual void InitNew();
	virtual Object *Clone();
	virtual Object *deepclone();
	Object *DeepClone();

	//---- change propagation
	void AddObserver(Object*);
	Object* RemoveObserver(Object*);
	virtual void Send(int id= cIdNone, int part= cPartAnyChange, void *vp= 0);
	virtual void DoObserve(int id, int part, void *vp, Object *op);
	void Changed();
	Iterator *GetObserverIter();
	void DelayChanges();
	void FlushChanges();
	virtual bool PrintOnWhenObserving(Object*);

	//---- error handling
	virtual void DoError(int level, const char *location, const char *fmt, va_list va);
	void Error(const char *method, const char *msgfmt, ...);
	void SysError(const char *method, const char *msgfmt, ...);
	void Warning(const char *method, const char *msgfmt, ...);
	void Fatal(const char *method, const char *msgfmt, ...);

	//---- fire walls
	void AbstractMethod(const char*);
	void MayNotUse(const char*);
	Object *guard(Class*);
#   define Guard(p,name) ((name*)(p)->guard(Meta(name)))

	//---- inspecting, debugging
	void Inspect(bool block= FALSE);
	void EditSource(bool declaration);
	virtual void InspectorId(char *buf, int bufSize);
	virtual void CollectParts(Collection*);
	void ScanMembers(u_long, ...);
	// direct (unchecked) access to instance variables; returns address of variable
	void* GetAddrOf(const char *varname, Class *&cl);

protected:
	static int _an;
	static AccessMembers *_accessMembers;

protected:
	//---- overridden if observers are stored in the object itself
	virtual Collection *MakeObserverColl();
	virtual Collection *GetObservers();
	virtual void DestroyObserverColl();
	virtual void SetObserverColl(Collection *);
	void CleanupObservers(Collection *);

private:
	Object(const Object&);

private:
	u_int flags_;
};

inline IStream &operator>> (IStream &s, Object *&op)
	{ return ::LoadPtr(s, op, Object::isa); }
inline Class *_Type(Object*)
	{ return Object::isa; }
inline void *Object::operator new(size_t sz)
	{ return Storage::ObjectAlloc(sz); }
inline void Object::operator delete(void *vp)
	{ delete vp; }
inline void Object::SetFlag(int f)
	{ flags_|= f & cFlagMask; }
inline void Object::ResetFlag(int f)
	{ flags_&= ~(f & cFlagMask); }
inline bool Object::TestFlag(int f)
	{ return (bool) ((flags_ & f) != 0); }
inline void Object::InvertFlag(int f)
	{ flags_^= f & cFlagMask; }
inline void Object::MarkAsDeleted()
	{ SetFlag(eObjIsDeleted); }
inline bool Object::IsDeleted()
	{ return TestFlag(eObjIsDeleted); }
inline void Object::SetVisited()
	{ flags_|= cObjVisited; }
inline void Object::ClearVisited()
	{ flags_&= ~cObjVisited; }
inline bool Object::IsObserved()
	{ return TestFlag(cObjIsObserved); }
inline bool Object::IsShared()
	{ return TestFlag(cObjIsShared); }
inline void Object::SetShared()
	{ SetFlag(cObjIsShared); }
inline OStream &operator<< (OStream &os, Object &op)
	{ return op.PrintOn(os); }
inline IStream &operator>> (IStream &is, Object &op)
	{ return op.ReadFrom(is); }
inline void Object::Changed()
	{ if (TestFlag(cObjIsObserved)) Send(); }

//---- DoDelayChanges ----------------------------------------------------------

// to delay the calls to Changed and ChangedWhat an instance of the class
// DelayChanges can be declared
// eg: Text::Cut() { DelayChanges dc(this); ... }

class DoDelayChanges {
	Object *op;
public:
	DoDelayChanges(Object *o)
		{ op= o; op->DelayChanges(); }
	~DoDelayChanges()
		{ op->FlushChanges(); }
	void Use()
		{ }
};

//---- MetaDef macros ----------------------------------------------------------

#define Meta(name) name::isa
#define SMeta(name) _NAME2_(__isa__,name)

#define MetaDef(name) \
	name(_Object_dummy*); \
	static Class *isa; \
	Class *IsA() const; \
	friend char *_NAME2_(name,DeclFileName)(char *p_= __FILE__) \
		{ return p_; } \
	friend int _NAME2_(name,DeclFileLine)(int i_= __LINE__) \
		{ return i_; } \
	friend IStream &operator>> (IStream &s_, name *&op_) \
		{ return LoadPtr(s_, (Object*&)op_, Meta(name)); } \
	void Members(AccessMembers*); \
	friend Class *_Type(name*) \
		{ return Meta(name); }

#endif

